package com.tom.demo.common.config;

import com.tom.demo.security.JwtAuthenticationTokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * @Author: LanQiang
 * @Date: 2023/01/16/15:37
 * @Description: SpringSecurity2.7以下，2.7以上有变化核心配置类
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解权限配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 注入自定义的过滤器
     * @return
     */
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 注入认证过程中权限不足出现的异常
     */
    @Resource
    private AccessDeniedHandler accessDeniedHandler;
    /**
     * 认证失败，抛出异常
     */
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;


    /**
     * anonymous():未登录的人才可以访问，已经登陆的人不能访问
     * authenticated():必须登陆才可以访问
     * permitAll():不管登没登陆，都可以访问
     * denyAll():拒绝全部
     * hasAnyAuthority(String....auth)必须满足这些权限中一种才能访问
     * hasAnyRole(String....authRole)必须满足这些角色中一种才能访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //配置异常处理器
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)//(身份失效，抛出异常会被它捕获，密码或者用户名错误，会被全局拦截捕获)
                .accessDeniedHandler(accessDeniedHandler)//(认证过权限不足出现的异常)
                .and()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()//开始定义接口路径的匹配规则
                .antMatchers("/authentication/login").anonymous()
                .antMatchers("/authentication/logout").authenticated()
                .antMatchers(
                        "/static/**",
                        "/swagger-ui.html",
                        "/favicon.ico",
                        "/swagger/**",
                        "/webjars/**",
                        "/swagger-resources/**",
                        "/v2/**",
                        "/configuration/security",
                        "/configuration/ui",
                        "/doc.html",
                        "/common/upload",
                        "/common/download",
                        "/public/**",
                        "/index",
                        "/",
                        "/static/**/**",
                        "/templates/**",
                        "/model/**/**",
                        "/editor/**",
                        "/activiti/**/**",
                        "/error",
                        "/api/thirdParty")
                .permitAll()
                .anyRequest()
                .authenticated()//除上面外的所有请求全部需要认证即可访问
                .and()
                //将我们自定义的过滤器放在UsernamePasswordAuthenticationFilter之前
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .cors();//允许跨域
    }

    /**
     * 📝Authentication：存储了认证信息，代表当前登录用户
     * 📝SeucirtyContext：上下文对象，用来获取Authentication
     * 📝SecurityContextHolder：上下文管理对象，用来在程序任何地方获取SecurityContext
     * @param authenticationConfiguration
     * 📝Authentication,里面又有三个不同部分：
     * 1:📝Principal：用户信息，没有认证时一般是用户名，认证后一般是用户对象
     * 2:📝Credentials：用户凭证，一般是密码
     * 3:📝Authorities：用户权限
     * @return AuthenticationManager
     */
    //TODO 当通过查询数据库之后，得到当前认证通过的用户信息，会全部存入到Authentication对象中，我们可以实现并且获取它
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
    /**
     * 创建BCryptPasswordEncoder注入容器
     * 此bean主要用于对密码加密和验证密码的正确行
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }



}